namespace ManoMachine.Emulator.Emulator
{
    using System;
    using System.Collections.Generic;

    using ManoMachine.Emulator.CPU;

    using ZiadSpace.Util;

    public static class InstructionsBusket
    {
        static InstructionsBusket()
        {
            // set instruction registers dictionary
            RegisterInstruction = new Dictionary<string, Action<Emulator>>();
            RegisterInstruction.Add("7800", CLA);
            RegisterInstruction.Add("7400", CLE);
            RegisterInstruction.Add("7200", CMA);
            RegisterInstruction.Add("7100", CME);
            RegisterInstruction.Add("7080", CIR);
            RegisterInstruction.Add("7040", CIL);
            RegisterInstruction.Add("7020", INC);
            RegisterInstruction.Add("7010", SPA);
            RegisterInstruction.Add("7008", SNA);
            RegisterInstruction.Add("7004", SZA);
            RegisterInstruction.Add("7002", SZE);
            RegisterInstruction.Add("7001", HLT);
            RegisterInstruction.Add("f800", INP);
            RegisterInstruction.Add("f400", OUT);
            RegisterInstruction.Add("f200", SkI);
            RegisterInstruction.Add("f100", SkO);
            RegisterInstruction.Add("f080", ION);
            RegisterInstruction.Add("f040", IOF);

        }


        #region Public register instruction

        public static void CLA(Emulator context)
        {
            context.Cpu.ComputerRegisters[Registers.AC] = 0;
            context.SaveCurrentT("T4");
        }
        //------
        public static void CLE(Emulator context)
        {
            context.Cpu.ComputerFlags[Flags.E] = false;
            context.SaveCurrentT("T4");
        }
        //---
        public static void CMA(Emulator context)
        {
            context.Cpu.ComputerRegisters[Registers.AC] = (short)(~context.Cpu.ComputerRegisters[Registers.AC]);
            context.SaveCurrentT("T4");
        }//---
        public static void CME(Emulator context)
        {
            context.Cpu.ComputerFlags[Flags.E] = !context.Cpu.ComputerFlags[Flags.E];
            context.SaveCurrentT("T4");
        }
        //---
        public static void CIR(Emulator context)
        {
            var E = context.Cpu.ComputerFlags[Flags.E];
            var AC = context.Cpu.ComputerRegisters[Registers.AC];

            AC = (short)(AC >> 1);
            AC = BitHelper.SetBit(AC, 16, E);
            E = BitHelper.GetBits(AC, 1) == 1;

            context.Cpu.ComputerRegisters[Registers.AC] = AC;
            context.Cpu.ComputerFlags[Flags.E] = E;

            context.SaveCurrentT("T4");
        }

        //---
        public static void CIL(Emulator context)
        {
            var E = context.Cpu.ComputerFlags[Flags.E];
            var AC = context.Cpu.ComputerRegisters[Registers.AC];

            AC = (short)(AC << 1);
            AC = BitHelper.SetBit(AC, 1, E);
            E = BitHelper.GetBits(AC, 16) == 1;

            context.Cpu.ComputerRegisters[Registers.AC] = AC;
            context.Cpu.ComputerFlags[Flags.E] = E;

            context.SaveCurrentT("T4");
        }
        //---
        public static void INC(Emulator context)
        {
            context.Cpu.ComputerRegisters[Registers.AC]++;
            context.SaveCurrentT("T4");
        }
        //---
        public static void SPA(Emulator context)
        {
            if (!BitHelper.IsNegative(context.Cpu.ComputerRegisters[Registers.AC]))
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");
        }
        //---
        public static void SNA(Emulator context)
        {
            if (BitHelper.IsNegative(context.Cpu.ComputerRegisters[Registers.AC]))
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");
        }
        //---
        public static void SZA(Emulator context)
        {
            if (context.Cpu.ComputerRegisters[Registers.AC] == 0)
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");
        }
        //---
        public static void SZE(Emulator context)
        {
            if (!context.Cpu.ComputerFlags[Flags.E])
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");
        }
        //---
        public static void HLT(Emulator context)
        {
            context.Cpu.ComputerFlags[Flags.S] = false;
            context.SaveCurrentT("T4");
        }
        #region Input output instructions
        //---
        public static void INP(Emulator context)
        {
            BitHelper.SetBits(
                context.Cpu.ComputerRegisters[Registers.AC], context.Cpu.ComputerRegisters[Registers.INPR], 1, 8);
            context.Cpu.ComputerFlags[Flags.FGI] = false;

            context.SaveCurrentT("T4");
        }
        //---
        public static void OUT(Emulator context)
        {
            BitHelper.SetBits(
               context.Cpu.ComputerRegisters[Registers.OUTR], context.Cpu.ComputerRegisters[Registers.AC], 1, 8);
            context.Cpu.ComputerFlags[Flags.FGO] = false;

            context.SaveCurrentT("T4");
        }
        //---
        public static void SkI(Emulator context)
        {
            if (context.Cpu.ComputerFlags[Flags.FGI])
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");

        }
        //---
        public static void SkO(Emulator context)
        {
            if (context.Cpu.ComputerFlags[Flags.FGO])
            {
                context.Cpu.ComputerRegisters[Registers.PC]++;
            }
            context.SaveCurrentT("T4");
        }
        //---
        public static void ION(Emulator context)
        {
            context.Cpu.ComputerFlags[Flags.IEN] = true;
            context.SaveCurrentT("T4");
        }
        //---
        public static void IOF(Emulator context)
        {
            context.Cpu.ComputerFlags[Flags.IEN] = false;
            context.SaveCurrentT("T4");
        } 
        #endregion
        #endregion

        #region memory instructions

        public static void AND(Emulator context)
        {
            // T4
            context.Cpu.ComputerRegisters[Registers.DR] =
                context.Memory.Read((ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.SaveCurrentT("T4");

            // T5
            context.Cpu.ComputerRegisters[Registers.AC] =
                (short)(context.Cpu.ComputerRegisters[Registers.AC] & context.Cpu.ComputerRegisters[Registers.DR]);
            context.SaveCurrentT("T5");

        }
        //----
        public static void ADD(Emulator context)
        {
            // T4
            context.Cpu.ComputerRegisters[Registers.DR] =
                context.Memory.Read((ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.SaveCurrentT("T4");

            // T5
            // this part nees some refinment
            try
            {
                // if there are no carry
                context.Cpu.ComputerRegisters[Registers.AC] = (short)checked(context.Cpu.ComputerRegisters[Registers.DR] + context.Cpu.ComputerRegisters[Registers.AC]);
                context.Cpu.ComputerFlags[Flags.E] = false;
                context.SaveCurrentT("T5");


            }
            catch (Exception)
            {
                // if there are a carry
                context.Cpu.ComputerRegisters[Registers.AC] = (short)(context.Cpu.ComputerRegisters[Registers.DR] + context.Cpu.ComputerRegisters[Registers.AC]);
                context.Cpu.ComputerFlags[Flags.E] = true;
                context.SaveCurrentT("T5");

            }
        }
        //----
        public static void LDA(Emulator context)
        {
            // T4
            context.Cpu.ComputerRegisters[Registers.DR] =
                context.Memory.Read((ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.SaveCurrentT("T4");

            // T5
            context.Cpu.ComputerRegisters[Registers.AC] = context.Cpu.ComputerRegisters[Registers.DR];
            context.SaveCurrentT("T5");
        }
        //----
        public static void STA(Emulator context)
        {
            // T4
            context.Memory.Write(context.Cpu.ComputerRegisters[Registers.AC], (ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.SaveCurrentT("T4");
        }
        //----
        public static void BUN(Emulator context)
        {
            // T4
            context.Cpu.ComputerRegisters[Registers.PC] = context.Cpu.ComputerRegisters[Registers.AR];
            context.SaveCurrentT("T4");
        }
        //----
        public static void BSA(Emulator context)
        {
            // T4
            context.Memory.Write(context.Cpu.ComputerRegisters[Registers.PC], (ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.Cpu.ComputerRegisters[Registers.AR]++;
            context.SaveCurrentT("T4");

            // T5
            context.Cpu.ComputerRegisters[Registers.PC] = context.Cpu.ComputerRegisters[Registers.AR];
            context.SaveCurrentT("T5");
        }
        //----
        public static void ISZ(Emulator context)
        {
            // T4
            context.Cpu.ComputerRegisters[Registers.DR] =
                context.Memory.Read((ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            context.SaveCurrentT("T4");

            // T5
            context.Cpu.ComputerRegisters[Registers.DR]++;
            context.SaveCurrentT("T4");

            // T6
            context.Memory.Write(context.Cpu.ComputerRegisters[Registers.DR], (ushort)context.Cpu.ComputerRegisters[Registers.AR], AddressModes.Direct);
            if (context.Cpu.ComputerRegisters[Registers.DR] == 0)
            { context.Cpu.ComputerRegisters[Registers.PC]++; }
            context.SaveCurrentT("T6");
        }
        #endregion

        public static Dictionary<string, Action<Emulator>> RegisterInstruction
        {
            get;
            set;
        }
    }
}